<?php
/**
 * @file 3gplex/includes/tools.inc
 *
 * Low level UBICAST/3GPLEX API.
 */

class plex_logger
{
	private $obj;

	function __call($method, $args)
	{
		plex::notice("{{ ".get_class($this->obj)."->${method}(".join(", ", $args).") }}");
		return call_user_func_array(array($this->obj, $method), $args);
	}

	function __get($name)
	{
		return $this->obj->$name;
	}

	function __set($name, $value)
	{
		return $this->obj->name = $value;
	}

	function __construct(&$obj)
	{
		$this->obj = $obj;
	}
}

class plex_static_cacher
{
	private $obj;
	private $methods;

	function __call($method, $args)
	{
		static $cache = array();
		if (in_array($method, $this->methods)) {
			$sign = serialize($args);
			if (!isset($cache[$method][$sign])) {
				$cache[$method][$sign] = call_user_func_array(array($this->obj, $method), $args);
			}
			return $cache[$method][$sign];
		}
		return call_user_func_array(array($this->obj, $method), $args);
	}
	
	function __construct(&$obj, array $methods)
	{
		$this->obj = $obj;
		$this->methods = $methods;
	}
}




/**
 * Low level UBICAST/3GPLEX API.
 */
abstract class plex {

	/**
	 * Warns on screen and in the logs with i18n enabled message.
	 */
	static function warn($msg, $args=null)
	{
		if ($args) {
			drupal_set_message(t($msg, $args), 'warning');
			watchdog('3Gplex', t($msg, $args), WATCHDOG_WARNING);
		} else {
			drupal_set_message(t($msg), 'warning');
			watchdog('3Gplex', t($msg), WATCHDOG_WARNING);
		}
	}
	
	/**
	 * Notices on screen and in the logs with i18n enabled message.
	 */
	static function notice($msg, $args=null)
	{
		if ($args) {
			drupal_set_message(t($msg, $args));
			watchdog('3Gplex', t($msg, $args));
		} else {
			drupal_set_message(t($msg));
			watchdog('3Gplex', t($msg));
		}
	}
	
	/**
	 * Checks an array for indexes; returning false if the array doesn't hold some indexes and
	 * there's no known default value; setting to given default value if the array doesn't hold
	 * index but there's a default.
	 */
	static function check_array(array $array, array $indexes)
	{
		foreach ($indexes as $index=>$default) {
			if (!isset($array[$index])) {
				if (is_null($default)) return false;
				$array[$index] = $default;
			}
		}
		return true;
	}
	
	/**
	 * Configure a module.
	 */
	static function configure_module($mod, $module_plexer = null, $module_plexure = null)
	{
		if (!method_exists('plex_'.$mod, 'configure_xml')) return false;
		$module_plexer = array('plex_'.$mod, 'configure_xml');
		
		if ($module_plexure === null) {
			$module_plexure = './'.drupal_get_path('module', 'drupalplex').'/config/'.$mod.'.xml';
		}
		if (is_dir($module_plexure)) {
			$array = file_scan_directory($module_plexure, $mod.'.xml');
			if (count($array) > 0) {
				$dbmod->plexure_path = $array[0]['filename'];
			}
		} else if (is_file($module_plexure)) {
			$dbmod->plexure_path = $module_plexure;
		};
		
		if (call_user_func($module_plexer, $dbmod->plexure_path) !== false) {
			$dbmod->plexed = 1;
			$dbmod->update();
		}
		return false;
	}

	/**
	 * Requires a specific (set of) module(s) to be installed, loading it's API and
	 * configuring it if necessary.
	 *
	 * @note
	 * If giving more than one module to require and there's a failure you won't know
	 * which one failed to install/enable.
	 */
	static function require_modules($modules, $module_plexer = null, $module_plexure = null)
	{
		$modules = (array)$modules;
		foreach ((array)$modules as $mod) {
			/*
			 * module_exists verifies a module is both installed
			 * and enabled.
			 */
			if (!module_exists($mod->module)) {
				/*
				 * drupal_install_modules does not check modules'
				 * requirements.
				 */
				if (drupal_check_module($mod->module) === false) {
					return false;
				}
				/*
				 * drupal_install_modules does verify a module is
				 * already installed itself.
				 */
				drupal_install_modules((array)$mod->module);
				/*
				 * However it does call module_enable only if installing.
				 */
				module_enable((array)$mod->module);
				if (!module_exists($mod->module)) return false;
			}
			$res = $mod->configure($plexer, $plexure);
		}
		return true;
	}
	
	/**
	 * Converts a SimpleXML object to an array of properties recursively.
	 *
	 * Any value should it be numeric is converted to a string.
	 */
	static function simplexml2array(SimpleXMLElement $xml)
	{
		$content = array();
		foreach ($xml->children() as $key=>$value) {
			$new = plex::simplexml2array($value);
			
			if (!isset($content[$key])) {
				$content[$key] = $new;
			} elseif (is_array($content[$key])) {
				$content[$key][] = $new;
			} else {
				$content[$key] = array($content[$key], $new);
			}
		}
		return empty($content)? (string)$xml : $content;
	}

	/**
	 * Returns an array of properties reading an xml file or null if failing to do so.
	 */
	static function xmlfile2array($path)
	{
		$xmltree = simplexml_load_file($path);
		if ($xmltree === false) {
			warn("Failed to read field definition file '%file'", array('%file'=>$file));
			return null;
		}
		return simplexml2array($xmltree);
	}
}
